package Composotion;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;

import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MultiHashtable;

import utils.ClassDiagramFileReader;
import utils.ClassDiagramFileWriter;

import ClassPack.Class;
import ClassPack.ClassDiagram;
import algorithms.ClassGrader;
import algorithms.EvenDumberGrader;
import algorithms.IheritingClassGrader;
import algorithms.InheritanceGrader;
import algorithms.InheritanceSuperClasseGrader;
import algorithms.PlainDumbGrader;
import algorithms.ReferencesCountGrader;
import algorithms.SyntacticMatchers.ClassNameGrader;
import algorithms.SyntacticMatchers.WordNet;
import basketMatcher.Basket;
import basketMatcher.BasketMatcher;


/**
 * @author ofer
 * creates the meta class after all processing and calculations were made/
 */

public class MetaClassDiagramComposer {

	public static boolean WRITETOXML = true;

	
	private BasketMatcher m_basketMatcher ;
	private Vector<Basket> m_classDiagramBaskets = new Vector<Basket> () ;
	
	public MetaClassDiagramComposer() {
		
		m_basketMatcher = new BasketMatcher();
		m_classDiagramBaskets = new Vector<Basket>();
	}

	/**
	 * 	the main function for the meta uml creation
	 */
	@SuppressWarnings("unchecked")
	public void CreateClassDiagram(){
		
		ClassDiagram result_classDiagram = new ClassDiagram();
//		Iterator itr = m_basketMatcher.getM_sorted_baskets().values().iterator();
		Set<Double> KeySet = m_basketMatcher.getM_sorted_baskets().keySet();
//		while (  itr.hasNext()) {
		
		while (!m_basketMatcher.getM_sorted_baskets().isEmpty()){
			
			Basket first_basket = m_basketMatcher.getM_sorted_baskets().highestBasket(); 
			Class c = CreateMetaClassFromBasket(first_basket);
			result_classDiagram.addClass(c , first_basket);
						
			remove_all_contained_baskets(first_basket );
		}
		result_classDiagram.connectClassesToOtherClasses();
		result_classDiagram.connectClassesInterfaces();
		result_classDiagram.setName("new_class_diagram");
		System.out.println("result class diagram : \n") ;
		System.out.println(result_classDiagram);
		if ( WRITETOXML) {
			ClassDiagramFileWriter fw = new 
			ClassDiagramFileWriter(result_classDiagram , result_classDiagram.getName() + ".xml");
			fw.CreateXMLroot();
			fw.WriteXMLtoFIle();
		}

		
	}
	/**
	 * 	starts the calculation at the basket matcher
	 */
	public void CalcBaskets() {
		m_basketMatcher.createBaskets();
		m_basketMatcher.gradeAllbaskets();
	}


	/**
	 * @param first_basket 
	 * 
	 * removes all baskets that contain class that are also contained in first_basket.
	 * based on the assumption that each class should apear only once.
	 * @param d 
	 */
	@SuppressWarnings("unchecked")
	private void remove_all_contained_baskets(Basket first_basket) {
		
		Double last_key = m_basketMatcher.getM_sorted_baskets().lastKey();
		Double current_traversed_key = m_basketMatcher.getM_sorted_baskets().firstKey();
		
		while ( last_key >= current_traversed_key ){
		// the older basket vector
		Vector<Basket> newv = 
			getBasketFilteredVector(first_basket, current_traversed_key);
		m_basketMatcher.getM_sorted_baskets().remove(current_traversed_key);
		m_basketMatcher.getM_sorted_baskets().putVector(current_traversed_key, newv);
		
			current_traversed_key = m_basketMatcher.getM_sorted_baskets().nextKeyInTree(current_traversed_key);
		}
		


	}

	private Vector<Basket> getBasketFilteredVector(Basket first_basket,
			Double current_traversed_key) {
		Vector<Basket> oldv = m_basketMatcher.getM_sorted_baskets().get(current_traversed_key);
		Vector<Basket> newv = new Vector<Basket>();
		
		for (Basket b : oldv ) {
			if (!b.containsClassesFrom(first_basket)){
				newv.add(b);
			}
		}
		return newv;
	}
	
	/**
	 * @param first_basket
	 * @return	a new class instance
	 * 
	 *  creates the class instances from attributes used within 
	 */
	private Class CreateMetaClassFromBasket(Basket first_basket) {
		
		Class c = new Class() ;
		setNameToClass(c , first_basket ) ;
		addMethodsToClass( c,  first_basket ) ;
		addFiedlsToClass ( c , first_basket) ; 
		
		System.out.println(c);
		return c;
	}


	private void setNameToClass(Class c, Basket first_basket) {
		c.setM_class_name(first_basket.getM_classes().firstElement().getM_class_name());
	}

	/**
	 * @param c the new constructed class
	 * @param first_basket 
	 * 
	 *  adds fields to the newly created class.
	 */
	
	private void addFiedlsToClass(Class c, Basket first_basket) {

		for ( Class class1 : first_basket.getM_classes() ) {
			for ( Class class2 : first_basket.getM_classes()) {
				if ( ! class1.equals(class2)) {
				
					addMutualFieldsToNewClass(c , class1 , class2);
				}
			}
		}
	}


	private void addMutualFieldsToNewClass(Class c, Class class1, Class class2) {
		// TODO implemet fields in the class
		
	}


	/**
	 * @param c - target created class.
	 * @param first_basket basket from which the new class is created from.
	 * 
	 *  adds new methods to the new class. a method is added if a syntactic match 
	 *  between two methods from classes in the basket exists. 
	 */
	
	
	private void addMethodsToClass(Class c, Basket first_basket) {

		for ( Class class1 : first_basket.getM_classes() ) {
			for ( Class class2 : first_basket.getM_classes()) {
				if ( ! class1.equals(class2)) {
					addMutualMethodsToNewClass(c , class1 , class2);
				}
			}
		}
	}


	/**
	 * @param c - the new constucted class 
	 * @param class1 - a class from the basket 
	 * @param class2 - a class from the basket 
	 * 
	 * a helper method for addMethodsToClass.
	 */
	private void addMutualMethodsToNewClass(Class c, Class class1, Class class2) {

		for (String method1 : class1.getM_methods()) { 
			for (String method2 : class2.getM_methods()) { 
//				if (( method1.startsWith("get") || method1.startsWith("set") ) &
//					(method2.startsWith("get") || method2.startsWith("set"))) {
//					String method1_postfix =  
				String temp_method1 = trimGetAndSet(method1);
				String temp_method2 = trimGetAndSet(method2);
				// if there is a syntactic connection between the words
				if 
				(WordNet.countMatchingRelatedWords(temp_method1, temp_method2) > 0 ){
					c.addMethod(method1);
				}
			}
					
			}
		}

	
	public void addClassDiagram(ClassDiagram cd ){
		m_basketMatcher.addClassDiagram(cd);
	}
	public void addAlgorithm(double ratio , ClassGrader cg) {
		m_basketMatcher.addAlgorithm(ratio, cg) ;
	}

	private String trimGetAndSet(String method1) {
		// TODO implement trimming of get and set.
		return method1;
	}
		

	
// TODO bug : method and field adding is done twice : need to traverse every couple only once.
	


public static void main(String [] args ) {
	
	ClassDiagram cd1 = new ClassDiagramFileReader("Sam1.xml").CreateClassDiagram();
	ClassDiagram cd2 = new ClassDiagramFileReader("Sam2.xml").CreateClassDiagram();
	
	MetaClassDiagramComposer composer = 
		new MetaClassDiagramComposer();
	
	composer.addClassDiagram(cd1);
	composer.addClassDiagram(cd2);
	
//	composer.addAlgorithm(0.5, new ClassNameGrader());
	composer.addAlgorithm(0.5, new InheritanceSuperClasseGrader());
	composer.addAlgorithm(0.5, new ReferencesCountGrader());
	composer.addAlgorithm(0.5, new InheritanceGrader());
	composer.addAlgorithm(0.5, new IheritingClassGrader());
	composer.addAlgorithm(0.5, new PlainDumbGrader());
	
	composer.CalcBaskets();
	composer.CreateClassDiagram();
	
	
	
	
	
}
}
